﻿/*
 * Copyright (c) 2011, The Chancellor, Masters and Scholars of the University of Oxford
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without modification, are 
 * permitted provided that the following conditions are met:
 *
 * - Redistributions of source code must retain the above copyright notice, this list of 
 *   conditions and the following disclaimer.
 *
 * - Redistributions in binary form must reproduce the above copyright notice, this list 
 *   of conditions and the following disclaimer in the documentation and/or other 
 *   materials provided with the distribution.
 *
 * Neither the name of University of Oxford nor the names of its contributors may be used
 * to endorse or promote products derived from this software without specific prior 
 * written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 
 * THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
 * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
 */

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Windows.Ink;
using System.Windows.Media.Imaging;

namespace Vreci.Metadata.Annotation
{
    public partial class ImageAnnotator : UserControl
    {
        #region Properties
        private bool Dragging
        {
            get;
            set;
        }
        private bool InMotion
        {
            get;
            set;
        }
        private Point DragOffset
        {
            get;
            set;
        }
        private Point LastMousePosition
        {
            get;
            set;
        }

        public Stroke NewStroke
        {
            get;
            set;
        }

        public StrokeCollection Strokes 
        {
            get
            {
                return annotator.Strokes;
            }

            set
            {
                annotator.Strokes = value;
            }
        }

        public InkPresenter AnnotationLayer
        {
            get
            {
                return annotator;
            }

            set
            {
                annotator = value;
            }
        }

        #endregion

        public ImageAnnotator()
        {
            InitializeComponent();
            DataContext = this;

            //msi.Width = ImageWidth;
            //msi.Height = ImageHeight;

            //annotator.Width = ImageWidth;
            //annotator.Height = ImageHeight;
            
            //SetupAnnotationLayer();

            Dragging = false;
        }

        #region MSI methods
        private void msi_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            if (Annotating) return;

            Dragging = false;

            if (InMotion)
            {
                InMotion = false;
                msi.ReleaseMouseCapture();
                return;
            }

            if (((Keyboard.Modifiers & ModifierKeys.Shift) == ModifierKeys.Shift))
            {
                ResetImageViewport();
                return;
            }

            Point p = msi.ElementToLogicalPoint(e.GetPosition(msi));
            double zoomFactor = (((Keyboard.Modifiers & ModifierKeys.Control) != ModifierKeys.Control) && ((Keyboard.Modifiers & ModifierKeys.Apple) != ModifierKeys.Apple)) ? 1.5 : 0.5;
            msi.ZoomAboutLogicalPoint(zoomFactor, p.X, p.Y);
        }

        private void msi_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            if (Annotating) return;

            Dragging = true;
            DragOffset = e.GetPosition(msi);
            LastMousePosition = msi.ViewportOrigin;
            msi.CaptureMouse();
        }

        private void msi_MouseMove(object sender, MouseEventArgs e)
        {
            if (!Dragging || Annotating) return;

            InMotion = true;

            Point newPoint = new Point();
            newPoint.X = LastMousePosition.X - (((e.GetPosition(msi).X - DragOffset.X) / msi.ActualWidth) * msi.ViewportWidth);
            newPoint.Y = LastMousePosition.Y - (((e.GetPosition(msi).Y - DragOffset.Y) / msi.ActualHeight) * msi.ViewportWidth);
            msi.ViewportOrigin = newPoint;
        }

        private void msi_MouseWheel(object sender, MouseWheelEventArgs e)
        {
            if (Annotating) return;

            Point p = msi.ElementToLogicalPoint(e.GetPosition(msi));
            double zoomFactor = (e.Delta < 0) ? 0.9 : 1.1;
            msi.ZoomAboutLogicalPoint(zoomFactor, p.X, p.Y);
            e.Handled = true;
        }

        private void ResetImageViewport()
        {
            msi.ViewportOrigin = new Point(0, 0);
            msi.ViewportWidth = 1;
        }

        private void msi_ViewportChanged(object sender, RoutedEventArgs e)
        {
            SetupAnnotationLayer();
        }
        
        #endregion

        #region InkPresenter methods
        private void SetupAnnotationLayer()
        {
            // This event is called during animations of the image.
            // Match the scaling of the canvas with the image
            Point viewportOrigin = msi.ViewportOrigin;
            double viewportWidth = msi.ViewportWidth;

            // The scale factor is just the inverse of the ViewportWidth
            double s = msi.ActualWidth / (ImageWidth * (ImageWidth / ImageHeight)); //take into account the original image width and height
            annotatorScale.ScaleX = (1 / viewportWidth) * s;
            annotatorScale.ScaleY = (1 / viewportWidth) * s;

            // The offset is calculated by finding the location of the origin of the dzi
            // in element coordinates.
            Point newO = LogicalToElement(new Point(), viewportOrigin,  viewportWidth, s);
            annotatorTranslate.X = newO.X;
            annotatorTranslate.Y = newO.Y;
            
        }

        private Point LogicalToElement(Point p, Point Origin, double Width, double s)
        {
            return new Point(((p.X - Origin.X) / Width) * (msi.ActualWidth),
            ((p.Y - Origin.Y) / Width) * (msi.ActualWidth));
        }
        
        private void annotator_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            annotator.CaptureMouse();
            NewStroke = new Stroke();
            NewStroke.DrawingAttributes.Color = StrokeColor;
            NewStroke.StylusPoints.Add(e.StylusDevice.GetStylusPoints(annotator));
            annotator.Strokes.Add(NewStroke);
        }

        private void annotator_LostMouseCapture(object sender, MouseEventArgs e)
        {
            NewStroke = null;
        }

        private void annotator_MouseMove(object sender, MouseEventArgs e)
        {
            if (NewStroke != null)
            {
                NewStroke.StylusPoints.Add(e.StylusDevice.GetStylusPoints(annotator));
            }
        }

        #endregion

        #region Dependency Properties

        public Color StrokeColor
        {
            get { return (Color)GetValue(StrokeColorProperty); }
            set { SetValue(StrokeColorProperty, value); }
        }

        public static readonly DependencyProperty StrokeColorProperty =
            DependencyProperty.Register("StrokeColor", typeof(Color), typeof(ImageAnnotator), new PropertyMetadata(Colors.Red));

        public DeepZoomImageTileSource Source
        {
            get
            {
                return (DeepZoomImageTileSource)GetValue(SourceProperty);
            }
            set { SetValue(SourceProperty, value); }
        }

        public static readonly DependencyProperty SourceProperty =
            DependencyProperty.Register("Source", typeof(DeepZoomImageTileSource), typeof(ImageAnnotator), new PropertyMetadata(null));

        public int ImageHeight
        {
            get { return (int)GetValue(ImageHeightProperty); }
            set
            {
                SetValue(ImageHeightProperty, value); //msi.Height = value; 
                annotator.Height = value;
            }
        }

        // Using a DependencyProperty as the backing store for ImageHeight.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty ImageHeightProperty =
            DependencyProperty.Register("ImageHeight", typeof(int), typeof(ImageAnnotator), new PropertyMetadata(0));

        public int ImageWidth
        {
            get { return (int)GetValue(ImageWidthProperty); }
            set
            {
                SetValue(ImageWidthProperty, value); //msi.Width = value; 
                annotator.Width = value;
            }
        }

        // Using a DependencyProperty as the backing store for ImageWidth.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty ImageWidthProperty =
            DependencyProperty.Register("ImageWidth", typeof(int), typeof(ImageAnnotator), new PropertyMetadata(0));

        #endregion

        private void annotator_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            RectangleGeometry rgeo = new RectangleGeometry();
            //rgeo.Rect = new Rect(0, 0, ImageWidth, ImageHeight);
            rgeo.Rect = new Rect(0, 0, annotator.ActualWidth, annotator.ActualHeight);
            annotator.Clip = rgeo;
        }

        public bool Annotating
        {
            get
            {
                return (bool)GetValue(AnnotatingProperty);
            }
            set
            {
                SetValue(AnnotatingProperty, value);
            }
        }

        // Using a DependencyProperty as the backing store for Annotating.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty AnnotatingProperty =
            DependencyProperty.Register("Annotating", typeof(bool), typeof(ImageAnnotator), new PropertyMetadata(false));

        private void msi_ImageOpenSucceeded(object sender, RoutedEventArgs e)
        {
            ResetImageViewport();

            //if (LayoutRoot.ActualWidth > ImageWidth)
            //{
            //    msi.Width = LayoutRoot.ActualWidth;
            //    msi.Height = LayoutRoot.ActualHeight;

            //    annotator.Width = msi.ActualWidth;
            //    annotator.Height = msi.ActualWidth / msi.AspectRatio;

            //}
            //else
            //{
            //    msi.Width = ImageWidth;
            //    msi.Height = ImageHeight;

            //    annotator.Width = ImageWidth;
            //    annotator.Height = ImageHeight;
            //}

            annotator.Width = ImageWidth;
            annotator.Height = ImageHeight;

            GeneralTransform transform2 = ContentContainer.TransformToVisual(ContentContainer);
            GeneralTransform transform = ContentContainer.TransformToVisual(msi);
            Point newO = transform.Transform(new Point(0,0));
            annotatorTranslate.X = newO.X;
            annotatorTranslate.Y = newO.Y;

            double scale = msi.ActualWidth / (ImageWidth * (ImageWidth / ImageHeight));
            annotatorScale.ScaleX = scale;
            annotatorScale.ScaleY = scale;

            //Make the zoomed size closer to original
            if (LayoutRoot.ActualWidth > ImageWidth)
            {
                //msi.ViewportWidth = msi.ActualWidth / (ImageWidth * (ImageWidth / ImageHeight));
                //double height = msi.ActualWidth / (ImageHeight * (ImageHeight / ImageWidth));
                //msi.ViewportOrigin = new Point(-((msi.ActualWidth / (ImageWidth * (ImageWidth / ImageHeight))) - 1) / 2, -((msi.ActualHeight / (ImageWidth * (ImageWidth / ImageHeight))) - 1) / 2);
            }
        }

            

    }
}
